﻿using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DllCallMeshAnalysisILSpy
{
    public class DeepCallAstVisitor : IAstVisitor
    {
        //Dictionary<string, List<string>> Usings = new Dictionary<string, List<string>>();
        Dictionary<string, List<string>> CallInfo = new Dictionary<string, List<string>>();

        List<string> MethodNames = new List<string>();

        /// <summary>
        /// 类型访问
        /// </summary>
        public Action<IType>? OnTypeVisit { get; set; }

        public void VisitType(IType type) 
        {
            if (OnTypeVisit == null)
                return;

            OnTypeVisit(type);
        }

        protected virtual void VisitChildren(AstNode node)
        {
            var childs = node.Children?.ToList();

            if (childs?.Any() == true)
            {
                foreach (var child in childs)
                {
                    child.AcceptVisitor(this);
                }
            }
        }

        public virtual void VisitNullNode(AstNode nullNode)
        {
        }

        public virtual void VisitSyntaxTree(SyntaxTree syntaxTree)
        {
            VisitChildren(syntaxTree);
        }

        public virtual void VisitComment(Comment comment)
        {
            VisitChildren(comment);
        }

        public virtual void VisitDocumentationReference(DocumentationReference documentationReference)
        {
            VisitChildren(documentationReference);
        }

        public virtual void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective)
        {
            VisitChildren(preProcessorDirective);
        }

        public virtual void VisitIdentifier(Identifier identifier)
        {
            VisitChildren(identifier);
        }

        public virtual void VisitCSharpTokenNode(CSharpTokenNode token)
        {
            VisitChildren(token);
        }

        public virtual void VisitPrimitiveType(PrimitiveType primitiveType)
        {
            VisitChildren(primitiveType);
        }

        public virtual void VisitComposedType(ComposedType composedType)
        {
            VisitChildren(composedType);
        }

        public virtual void VisitSimpleType(SimpleType simpleType)
        {
            VisitChildren(simpleType);
        }

        public virtual void VisitMemberType(MemberType memberType)
        {
            VisitChildren(memberType);
        }

        public virtual void VisitTupleType(TupleAstType tupleType)
        {
            VisitChildren(tupleType);
        }

        public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement)
        {
            VisitChildren(tupleTypeElement);
        }

        public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType)
        {
            VisitChildren(functionPointerType);
        }

        public virtual void VisitInvocationType(InvocationAstType invocationType)
        {
            VisitChildren(invocationType);
        }

        public virtual void VisitAttribute(ICSharpCode.Decompiler.CSharp.Syntax.Attribute attribute)
        {
            VisitChildren(attribute);
        }

        public virtual void VisitAttributeSection(AttributeSection attributeSection)
        {
            VisitChildren(attributeSection);
        }

        public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration)
        {
            VisitChildren(delegateDeclaration);
        }

        public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
        {
            VisitChildren(namespaceDeclaration);
        }

        public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
        {
            VisitChildren(typeDeclaration);
        }

        public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
        {
            VisitChildren(typeParameterDeclaration);
        }

        public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration)
        {
            VisitChildren(enumMemberDeclaration);
        }

        public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration)
        {
            VisitChildren(usingDeclaration);
        }

        public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration)
        {
            VisitChildren(usingDeclaration);
        }

        public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration)
        {
            VisitChildren(externAliasDeclaration);
        }

        public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
        {
            VisitChildren(constructorDeclaration);
        }

        public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer)
        {
            VisitChildren(constructorInitializer);
        }

        public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
        {
            VisitChildren(destructorDeclaration);
        }

        public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration)
        {
            VisitChildren(eventDeclaration);
        }

        public virtual void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration)
        {
            VisitChildren(eventDeclaration);
        }

        public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration)
        {
            VisitChildren(fieldDeclaration);
        }

        public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration)
        {
            VisitChildren(fixedFieldDeclaration);
        }

        public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer)
        {
            VisitChildren(fixedVariableInitializer);
        }

        public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
        {
            VisitChildren(indexerDeclaration);
        }

        public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
        {
            VisitChildren(methodDeclaration);

            //MethodNames.Add(methodDeclaration.Name);

            //var nameBlocks = new List<string>();

            //var nameSpace = methodDeclaration.GetParent<NamespaceDeclaration>();
            //if (nameSpace != null)
            //    nameBlocks.Add(nameSpace.Name);

            //var type = methodDeclaration.GetParent<TypeDeclaration>();
            //if (type != null)
            //    nameBlocks.Add(type.Name);

            //nameBlocks.Add(methodDeclaration.Name);

            //MethodNames.Add(string.Join(".", nameBlocks));
        }

        public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
        {
            VisitChildren(operatorDeclaration);
        }

        public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
        {
            VisitChildren(propertyDeclaration);
        }

        public virtual void VisitAccessor(Accessor accessor)
        {
            VisitChildren(accessor);
        }

        public virtual void VisitVariableInitializer(VariableInitializer variableInitializer)
        {
            VisitChildren(variableInitializer);
        }

        public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration)
        {
            VisitChildren(parameterDeclaration);
        }

        public virtual void VisitConstraint(Constraint constraint)
        {
            VisitChildren(constraint);
        }

        public virtual void VisitBlockStatement(BlockStatement blockStatement)
        {
            VisitChildren(blockStatement);
        }

        public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement)
        {
            VisitChildren(expressionStatement);
        }

        public virtual void VisitBreakStatement(BreakStatement breakStatement)
        {
            VisitChildren(breakStatement);
        }

        public virtual void VisitCheckedStatement(CheckedStatement checkedStatement)
        {
            VisitChildren(checkedStatement);
        }

        public virtual void VisitContinueStatement(ContinueStatement continueStatement)
        {
            VisitChildren(continueStatement);
        }

        public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement)
        {
            VisitChildren(doWhileStatement);
        }

        public virtual void VisitEmptyStatement(EmptyStatement emptyStatement)
        {
            VisitChildren(emptyStatement);
        }

        public virtual void VisitFixedStatement(FixedStatement fixedStatement)
        {
            VisitChildren(fixedStatement);
        }

        public virtual void VisitForeachStatement(ForeachStatement foreachStatement)
        {
            VisitChildren(foreachStatement);
        }

        public virtual void VisitForStatement(ForStatement forStatement)
        {
            VisitChildren(forStatement);
        }

        public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement)
        {
            VisitChildren(gotoCaseStatement);
        }

        public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement)
        {
            VisitChildren(gotoDefaultStatement);
        }

        public virtual void VisitGotoStatement(GotoStatement gotoStatement)
        {
            VisitChildren(gotoStatement);
        }

        public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement)
        {
            VisitChildren(ifElseStatement);
        }

        public virtual void VisitLabelStatement(LabelStatement labelStatement)
        {
            VisitChildren(labelStatement);
        }

        public virtual void VisitLockStatement(LockStatement lockStatement)
        {
            VisitChildren(lockStatement);
        }

        public virtual void VisitReturnStatement(ReturnStatement returnStatement)
        {
            VisitChildren(returnStatement);
        }

        public virtual void VisitSwitchStatement(SwitchStatement switchStatement)
        {
            VisitChildren(switchStatement);
        }

        public virtual void VisitSwitchSection(SwitchSection switchSection)
        {
            VisitChildren(switchSection);
        }

        public virtual void VisitCaseLabel(CaseLabel caseLabel)
        {
            VisitChildren(caseLabel);
        }

        public virtual void VisitSwitchExpression(SwitchExpression switchExpression)
        {
            VisitChildren(switchExpression);
        }

        public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
        {
            VisitChildren(switchExpressionSection);
        }

        public virtual void VisitThrowStatement(ThrowStatement throwStatement)
        {
            VisitChildren(throwStatement);
        }

        public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement)
        {
            VisitChildren(tryCatchStatement);
        }

        public virtual void VisitCatchClause(CatchClause catchClause)
        {
            VisitChildren(catchClause);
        }

        public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement)
        {
            VisitChildren(uncheckedStatement);
        }

        public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement)
        {
            VisitChildren(unsafeStatement);
        }

        public virtual void VisitUsingStatement(UsingStatement usingStatement)
        {
            VisitChildren(usingStatement);
        }

        public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement)
        {
            VisitChildren(variableDeclarationStatement);
        }

        public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
        {
            VisitChildren(localFunctionDeclarationStatement);
        }

        public virtual void VisitWhileStatement(WhileStatement whileStatement)
        {
            VisitChildren(whileStatement);
        }

        public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement)
        {
            VisitChildren(yieldBreakStatement);
        }

        public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement)
        {
            VisitChildren(yieldReturnStatement);
        }

        public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
        {
            VisitChildren(anonymousMethodExpression);
        }

        public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression)
        {
            VisitChildren(lambdaExpression);
        }

        public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression)
        {
            VisitChildren(assignmentExpression);
        }

        public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression)
        {
            VisitChildren(baseReferenceExpression);
        }

        public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
        {
            VisitChildren(binaryOperatorExpression);
        }

        public virtual void VisitCastExpression(CastExpression castExpression)
        {
            VisitChildren(castExpression);
        }

        public virtual void VisitCheckedExpression(CheckedExpression checkedExpression)
        {
            VisitChildren(checkedExpression);
        }

        public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression)
        {
            VisitChildren(conditionalExpression);
        }

        public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression)
        {
            VisitChildren(identifierExpression);
        }

        public virtual void VisitIndexerExpression(IndexerExpression indexerExpression)
        {
            VisitChildren(indexerExpression);
        }

        public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression)
        {
            VisitChildren(interpolatedStringExpression);
        }

        public virtual void VisitInterpolation(Interpolation interpolation)
        {
            VisitChildren(interpolation);
        }

        public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText)
        {
            VisitChildren(interpolatedStringText);
        }

        public virtual void VisitInvocationExpression(InvocationExpression invocationExpression)
        {
            VisitChildren(invocationExpression);

            var parent_method = invocationExpression.GetParent(n => n is MethodDeclaration) as MethodDeclaration;
            var parent_type = invocationExpression.GetParent(n => n is TypeDeclaration) as TypeDeclaration;
            if (parent_method != null && parent_type != null)
            {
                var methodName = $"{parent_type.Name}.{parent_method.Name}";
                var parent_namespace = invocationExpression.GetParent<NamespaceDeclaration>();
                if (parent_namespace != null)
                    methodName = $"{parent_namespace.NamespaceName}.{parent_type.Name}.{parent_method.Name}";

                if (CallInfo.TryGetValue(methodName, out var callList) != true)
                {
                    callList = new List<string>();
                    CallInfo[methodName] = callList;
                }

                var typeRef = invocationExpression.FindFistChild<TypeReferenceExpression>();
                var typeResolveResult = typeRef?.Annotation<TypeResolveResult>();
                if (typeResolveResult != null)
                {
                    callList.Add($"{typeResolveResult.Type.Namespace}.{invocationExpression.Target}");

                    VisitType(typeResolveResult.Type);
                }
                else 
                {
                    if (parent_namespace != null)
                    {
                        callList.Add($"{parent_namespace.NamespaceName}.{parent_type.Name}.{invocationExpression.Target}");
                    }
                    else 
                    {
                        callList.Add($"{parent_type.Name}.{invocationExpression.Target}");
                    }
                }
            }
        }

        public virtual void VisitDirectionExpression(DirectionExpression directionExpression)
        {
            VisitChildren(directionExpression);
        }

        public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
        {
            VisitChildren(memberReferenceExpression);

            //if (memberReferenceExpression.Target is TypeReferenceExpression typeReferenceExpression)
            //{
            //    typeReferenceExpression.AcceptVisitor(this);

            //    //var typeInfo = typeReferenceExpression.Annotation<TypeResolveResult>();
            //}
        }

        public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression)
        {
            VisitChildren(nullReferenceExpression);
        }

        public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
        {
            VisitChildren(objectCreateExpression);
        }

        public virtual void VisitDeclarationExpression(DeclarationExpression declarationExpression)
        {
            VisitChildren(declarationExpression);
        }

        public virtual void VisitRecursivePatternExpression(RecursivePatternExpression recursivePatternExpression)
        {
            VisitChildren(recursivePatternExpression);
        }

        public virtual void VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression)
        {
            VisitChildren(outVarDeclarationExpression);
        }

        public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression)
        {
            VisitChildren(anonymousTypeCreateExpression);
        }

        public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression)
        {
            VisitChildren(arrayCreateExpression);
        }

        public virtual void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression)
        {
            VisitChildren(parenthesizedExpression);
        }

        public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression)
        {
            VisitChildren(pointerReferenceExpression);
        }

        public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression)
        {
            VisitChildren(primitiveExpression);
        }

        public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression)
        {
            VisitChildren(sizeOfExpression);
        }

        public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression)
        {
            VisitChildren(stackAllocExpression);
        }

        public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression)
        {
            VisitChildren(thisReferenceExpression);
        }

        public virtual void VisitThrowExpression(ThrowExpression throwExpression)
        {
            VisitChildren(throwExpression);
        }

        public virtual void VisitTupleExpression(TupleExpression tupleExpression)
        {
            VisitChildren(tupleExpression);
        }

        public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression)
        {
            VisitChildren(typeOfExpression);
        }

        public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression)
        {
            VisitChildren(typeReferenceExpression);
        }

        public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
        {
            VisitChildren(unaryOperatorExpression);
        }

        public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression)
        {
            VisitChildren(uncheckedExpression);
        }

        public virtual void VisitQueryExpression(QueryExpression queryExpression)
        {
            VisitChildren(queryExpression);
        }

        public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause)
        {
            VisitChildren(queryContinuationClause);
        }

        public virtual void VisitQueryFromClause(QueryFromClause queryFromClause)
        {
            VisitChildren(queryFromClause);
        }

        public virtual void VisitQueryLetClause(QueryLetClause queryLetClause)
        {
            VisitChildren(queryLetClause);
        }

        public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause)
        {
            VisitChildren(queryWhereClause);
        }

        public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause)
        {
            VisitChildren(queryJoinClause);
        }

        public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause)
        {
            VisitChildren(queryOrderClause);
        }

        public virtual void VisitQueryOrdering(QueryOrdering queryOrdering)
        {
            VisitChildren(queryOrdering);
        }

        public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause)
        {
            VisitChildren(querySelectClause);
        }

        public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause)
        {
            VisitChildren(queryGroupClause);
        }

        public virtual void VisitAsExpression(AsExpression asExpression)
        {
            VisitChildren(asExpression);
        }

        public virtual void VisitIsExpression(IsExpression isExpression)
        {
            VisitChildren(isExpression);
        }

        public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression)
        {
            VisitChildren(defaultValueExpression);
        }

        public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression)
        {
            VisitChildren(undocumentedExpression);
        }

        public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression)
        {
            VisitChildren(arrayInitializerExpression);
        }

        public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier)
        {
            VisitChildren(arraySpecifier);
        }

        public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression)
        {
            VisitChildren(namedArgumentExpression);
        }

        public virtual void VisitNamedExpression(NamedExpression namedExpression)
        {
            VisitChildren(namedExpression);
        }

        public virtual void VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation)
        {
            VisitChildren(singleVariableDesignation);
        }

        public virtual void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation)
        {
            VisitChildren(parenthesizedVariableDesignation);
        }

        public virtual void VisitErrorNode(AstNode errorNode)
        {
            VisitChildren(errorNode);
        }

        public virtual void VisitPatternPlaceholder(AstNode placeholder, Pattern pattern)
        {
            VisitChildren(placeholder);
        }

        public virtual void VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression)
        {
            VisitChildren(withInitializerExpression);
        }
    }
}
